//本文件作者来自csdn https://blog.csdn.net/qq_39952971/article/details/130548647
//资源释放存在问题

#include <stdio.h>
#include <string.h>
#include "sm2_ka.h"
#include "sm3_with_preprocess.h"

unsigned char r_a[32] = {0xD4, 0xDE, 0x15, 0x47, 0x4D, 0xB7, 0x4D, 0x06, 0x49, 0x1C, 0x44,
    0x0D, 0x30, 0x5E, 0x01, 0x24, 0x00, 0x99, 0x0F, 0x3E, 0x39, 0x0C,
    0x7E, 0x87, 0x15, 0x3C, 0x12, 0xDB, 0x2E, 0xA6, 0x0B, 0xB3};
unsigned char r_b[32] = {0x7E, 0x07, 0x12, 0x48, 0x14, 0xB3, 0x09, 0x48, 0x91, 0x25, 0xEA,
    0xED, 0x10, 0x11, 0x13, 0x16, 0x4E, 0xBF, 0x0F, 0x34, 0x58, 0xC5,
    0xBD, 0x88, 0x33, 0x5C, 0x1F, 0x9D, 0x59, 0x62, 0x43, 0xD6};

void dump_hex(const char *str, unsigned char *hex_data, int len) {
    int i = 0;


    printf("%s", str);
    for (i = 0; i < len; i++) {
        if (i % 16 == 0) printf("\n");
        printf("0x%02x, ", hex_data[i]);
    }
    printf("\n");
}
void dump_bn(const char *str, BIGNUM* bn) {
    int i = 0;
    int len=0;
    unsigned char hex_data[32]={0};

    len=BN_num_bytes(bn);
    BN_bn2binpad(bn, hex_data, len);
    printf("%s", str);
    for (i = 0; i < len; i++) {
        if (i % 16 == 0) printf("\n");
        printf("0x%02x, ", hex_data[i]);
    }
    printf("\n");
}

EC_POINT * agreement_init(SM2KA *ka, unsigned char *pri, unsigned char *pub_x, unsigned char *pub_y, unsigned char *id, int id_len)
{
    BIGNUM *x = BN_new();
    BIGNUM *y = BN_new();

    ka->ctx = BN_CTX_new();
    ka->order = BN_new();
    ka->group = EC_GROUP_new_by_curve_name(NID_sm2);
    EC_GROUP_get_order(ka->group, ka->order, ka->ctx);

    ka->prik = BN_new();
    ka->P = EC_POINT_new(ka->group);

    BN_bin2bn(pri, 32, ka->prik);
    BN_bin2bn(pub_x, 32, x);
    BN_bin2bn(pub_y, 32, y);

    EC_POINT_set_affine_coordinates(ka->group, ka->P, x, y, ka->ctx);

    unsigned char pub_data[66]={0};
    EC_POINT_point2oct((const EC_GROUP *)(ka->group), ka->P, POINT_CONVERSION_UNCOMPRESSED, pub_data, 256, ka->ctx);
    int error_code = 0;
    if (error_code = sm3_digest_z(id,
                id_len,
                pub_data,
                ka->Z))
    {
        printf("Compute SM3 digest of leading data Z failed at %s, line %d!\n", __FILE__, __LINE__);
    }

    BN_free(x);
    BN_free(y);
    return ka->P;
}
void agreement_destrory(SM2KA *ka)
{
    BN_free(ka->prik);
    BN_free(ka->r);
    EC_POINT_free(ka->P);
    EC_POINT_free(ka->R);
    BN_free(ka->order);
    BN_CTX_free(ka->ctx);
    EC_GROUP_free(ka->group);
}

EC_POINT *gen_agreementdata(SM2KA *ka)
{
    ka->r = BN_new();
#if 0
    BN_rand_range(ka->r, ka->order);
#else
    BN_bin2bn(r_a, 32,ka->r);
#endif
    ka->R = EC_POINT_new(ka->group);
    EC_POINT_mul(ka->group, ka->R, ka->r, NULL, NULL, ka->ctx);
    dump_bn("ra:",ka->r);
    printf("点RA\n");
    dump_point(ka, ka->R);

    return ka->R;

}

void calc_x(SM2KA *ka, EC_POINT *R, BIGNUM **px)
{
    BIGNUM *x = BN_new();
    BIGNUM *bn = *px;
    EC_POINT_get_affine_coordinates(ka->group, R, x, NULL, ka->ctx);

    BIGNUM *w = BN_new();
    BN_set_word(w, 1);
    BN_lshift(w, w, ((BN_num_bits(ka->order) + 1) / 2 - 1));
    BN_nnmod(x, x, w, ka->ctx);
    BN_add(bn, x, w);

    BN_free(x);
    BN_free(w);

}
int gen_kdf(SM2KA *ka, EC_POINT *point, unsigned char *Z_peer, unsigned char *agreekey, int flag)
{
    int ret = 0;
    unsigned char kdfbuf[128] = {0};
    unsigned char kdfin[128] = {0};
    int rlen=16;
    BIGNUM *x = BN_new();
    BIGNUM *y = BN_new();

    EC_POINT_get_affine_coordinates_GFp(ka->group, point, x, y, ka->ctx);
    BN_bn2binpad(x, kdfbuf, 32);
    BN_bn2binpad(y, kdfbuf + 32, 32);
    if (flag) {
        memcpy(kdfbuf + 64, ka->Z, 32);
        memcpy(kdfbuf + 96, Z_peer, 32);
    } else {
        memcpy(kdfbuf + 64, Z_peer, 32);
        memcpy(kdfbuf + 96, ka->Z, 32);
    }
    memcpy(kdfin, kdfbuf, 128);
    GMT3kdf(kdfin, 128, agreekey, &rlen);
    if (rlen != 16)
    {
        ret = -1;
        printf("rlen error\n");
        goto end;
    }

end:
    BN_free(x);
    BN_free(y);
    return ret;

}

EC_POINT *gen_agreementdata_key(SM2KA *ka, EC_POINT *RA, EC_POINT *PA, unsigned char *Z_peer)
{
    EC_POINT *ret=NULL;
    BIGNUM *h = BN_new();
    EC_POINT *point = EC_POINT_new(ka->group);
    ka->r = BN_new();
#if 0
    BN_rand_range(ka->r, ka->order);
#else
    BN_bin2bn(r_b, 32,ka->r);
#endif
    ka->R = EC_POINT_new(ka->group);
    EC_POINT_mul(ka->group, ka->R, ka->r, NULL, NULL, ka->ctx);
    dump_bn("rb:",ka->r);
    printf("点RB\n");
    dump_point(ka, ka->R);

    BIGNUM *x = BN_new();
    BIGNUM *t = BN_new();
    calc_x(ka, ka->R, &x);
    dump_bn("x2`:",x);
    BN_mod_mul(t, x, ka->r, ka->order, ka->ctx);
    BN_mod_add(t, t, ka->prik, ka->order, ka->ctx);
    dump_bn("tb:",t);
    if (!(EC_POINT_is_on_curve(ka->group, RA, ka->ctx))) {
        printf("RA is not ECC point\n");
        ret = NULL;
        goto end;
    }
    BIGNUM *x1 = BN_new();
    calc_x(ka, RA, &x1);
    dump_bn("x1`:",x1);


    unsigned char kdfbuf[128] = {0};
    BN_CTX *bnctx = BN_CTX_new();
    EC_GROUP_get_cofactor(ka->group, h, bnctx);
    BN_mul(h, h, t, bnctx);
    EC_POINT_mul(ka->group, point, NULL, RA, x1, bnctx);
    /*
       printf("x1*RA:\n");
       dump_point(ka, point);
       */
    EC_POINT_add(ka->group, point, point, PA, bnctx);
    /*
       printf("x1*RA+PA:\n");
       dump_point(ka, point);
       */
    EC_POINT_mul(ka->group, point, NULL, point, h, bnctx);
    if (EC_POINT_is_at_infinity(ka->group, point)) {
        printf("V is 0 point\n");
        ret = NULL;
        goto end;
    }
    printf("点V:\n");
    dump_point(ka, point);
    unsigned char agreekey[16]={0};
    gen_kdf(ka, point, Z_peer, agreekey, 0);
    dump_hex("agreement key:",agreekey,16);
    ret = ka->R;

end:
    BN_free(x);
    BN_free(t);
    BN_free(x1);
    BN_free(h);
    EC_POINT_free(point);
    return ret;

}
int gen_key(SM2KA *ka, EC_POINT *RB, EC_POINT *PB, unsigned char *Z_peer)
{
    int ret = 0;
    BIGNUM *x = BN_new();
    BIGNUM *t = BN_new();
    BIGNUM *h = BN_new();
    calc_x(ka, ka->R, &x);
    BN_mod_mul(t, x, ka->r, ka->order, ka->ctx);
    BN_mod_add(t, t, ka->prik, ka->order, ka->ctx);

    if (!(EC_POINT_is_on_curve(ka->group, RB, ka->ctx))) {
        printf("RB is not ECC point\n");
        ret = -1;
        goto end;
    }
    calc_x(ka, RB, &x);

    unsigned char kdfbuf[128] = {0};
    EC_POINT *point = EC_POINT_new(ka->group);
    BN_CTX *bnctx = BN_CTX_new();
    EC_GROUP_get_cofactor(ka->group, h, bnctx);
    BN_mul(h, h, t, bnctx);
    EC_POINT_mul(ka->group, point, NULL, RB, x, bnctx);
    EC_POINT_add(ka->group, point, point, PB, bnctx);
    EC_POINT_mul(ka->group, point, NULL, point, h, bnctx);
    if (EC_POINT_is_at_infinity(ka->group, point)) {
        printf("V is 0 point\n");
        ret = -1;
        goto end;
    }
    //EC_POINT_get_affine_coordinates_GFp(ka->group, point, xU, yU, bnctx);

    printf("点U:\n");
    dump_point(ka, point);
    unsigned char agreekey[16]={0};
    gen_kdf(ka, point, Z_peer, agreekey, 1);
    dump_hex("agreement key:",agreekey,16);

end:
    BN_free(x);
    BN_free(t);
    BN_free(h);
    EC_POINT_free(point);
    return ret;
}
void dump_point(SM2KA *ka, EC_POINT *P)
{
    unsigned char str_x[32]={0};
    unsigned char str_y[32]={0};
    BIGNUM *y = BN_CTX_get(ka->ctx);
    BIGNUM *x = BN_CTX_get(ka->ctx);
    EC_POINT_get_affine_coordinates(ka->group, P, x, y, ka->ctx);

    dump_bn("-X",x);
    dump_bn("-Y",y);

    BN_free(x);
    BN_free(y);
}
#if 0

void SM2CS_init(SM2CS *cs)
{
    cs->ctx = BN_CTX_new();
    cs->order = BN_new();
    cs->ecGroup = EC_GROUP_new_by_curve_name(NID_sm2);
    EC_GROUP_get_order(cs->ecGroup, cs->order, cs->ctx);
    cs->k = BN_new();
    cs->pri_comp = BN_new();
    BN_rand_range(cs->pri_comp, cs->order);
    cs->pub_comp = EC_POINT_new(cs->ecGroup);
    EC_POINT_mul(cs->ecGroup, cs->pub_comp, cs->pri_comp, NULL, NULL, cs->ctx);
    cs->ComPubk = EC_POINT_new(cs->ecGroup);
    dump_bn("密钥分量:",cs->pri_comp);
}

void SM2CS_destrory(SM2CS *cs)
{
    BN_free(cs->k);
    EC_POINT_free(cs->ComPubk);
    BN_free(cs->order);
    BN_CTX_free(cs->ctx);
    EC_GROUP_free(cs->ecGroup);
}

EC_POINT *getPubComponent(SM2CS *cs)
{
    return cs->pub_comp;
}


void getCommonPubk(SM2CS *cs, EC_POINT *Peer, EC_POINT **getComPubk, unsigned char *pub)
{
    if(NULL!=Peer && NULL!=getComPubk) {
        EC_POINT *Ptmp = EC_POINT_new(cs->ecGroup);
        EC_POINT *point = EC_POINT_new(cs->ecGroup);
        EC_POINT_mul(cs->ecGroup, Ptmp, NULL, Peer, cs->pri_comp, cs->ctx);

        BIGNUM *yG = BN_CTX_get(cs->ctx);
        BIGNUM *xG = BN_CTX_get(cs->ctx);
        const EC_POINT *G = EC_GROUP_get0_generator(cs->ecGroup);
        EC_POINT_get_affine_coordinates(cs->ecGroup, G, xG, yG, cs->ctx);
        BN_set_negative(yG, 1);
        EC_POINT_set_affine_coordinates(cs->ecGroup, point, xG, yG, cs->ctx);
        EC_POINT_add(cs->ecGroup, cs->ComPubk, Ptmp, point, cs->ctx);
        EC_POINT_free(point);
        EC_POINT_free(Ptmp);
        *getComPubk = cs->ComPubk;

        unsigned char tmpbuf[256] = {0};
        int len = EC_POINT_point2oct((const EC_GROUP *)(cs->ecGroup), cs->ComPubk, POINT_CONVERSION_UNCOMPRESSED, pub, 256, cs->ctx);

    }

}

int signInitiator(SM2CS *cs, EC_POINT *Peer,EC_POINT **l_R,EC_POINT **l_Rt)
{
    int ret = -1;
    if ((l_R==NULL)||(l_Rt==NULL))
        return ret;
    cs->R = EC_POINT_new(cs->ecGroup);
    cs->Rt = EC_POINT_new(cs->ecGroup);

    BN_rand_range(cs->k, cs->order);
    dump_bn("Ka:",cs->k);
    EC_POINT_mul(cs->ecGroup, cs->R, cs->k, NULL, NULL, cs->ctx);
    EC_POINT_mul(cs->ecGroup, cs->Rt, NULL, Peer, cs->k, cs->ctx);
    printf("Ra:\n");
    dump_point(cs, cs->R);
    printf("Ra`:\n");
    dump_point(cs, cs->Rt);
    *l_R = cs->R;
    *l_Rt = cs->Rt;
    ret = 0;

    return ret;
}

int signPartner(SM2CS *cs, EC_POINT *Peer, EC_POINT *R_peer,EC_POINT *Rt_peer,EC_POINT **l_R,EC_POINT **l_Rt)
{
    int ret = 0;

    EC_POINT *tmp = EC_POINT_new(cs->ecGroup);
    BIGNUM *D = BN_new();
    BN_mod_inverse(D, cs->pri_comp, cs->order, cs->ctx);
    EC_POINT_mul(cs->ecGroup, tmp, NULL, Rt_peer, D, cs->ctx);
    ret = EC_POINT_cmp(cs->ecGroup, tmp, R_peer, cs->ctx);
    if(ret != 0)
    {
        printf("point R verify failed\n");
        return ret;
    }
    printf("point Ra verify sucessful\n");

    cs->R = EC_POINT_new(cs->ecGroup);
    cs->Rt = EC_POINT_new(cs->ecGroup);

    BN_rand_range(cs->k, cs->order);
    dump_bn("Kb:",cs->k);
    EC_POINT_mul(cs->ecGroup, cs->Rt, cs->k, NULL, NULL, cs->ctx);
    EC_POINT_mul(cs->ecGroup, cs->R, NULL, Peer, cs->k, cs->ctx);
    printf("Rb:\n");
    dump_point(cs, cs->R);
    printf("Rb`:\n");
    dump_point(cs, cs->Rt);
    *l_R = cs->R;
    *l_Rt = cs->Rt;

    return 0;
}

int signInitiatorAgreement(SM2CS *cs, EC_POINT *R_peer,EC_POINT *Rt_peer,const unsigned char *E,BIGNUM *st)
{
    int ret = 0;

    EC_POINT *tmp = EC_POINT_new(cs->ecGroup);
    EC_POINT_mul(cs->ecGroup, tmp, NULL, Rt_peer, cs->pri_comp, cs->ctx);
    ret = EC_POINT_cmp(cs->ecGroup, tmp, R_peer, cs->ctx);
    if(ret != 0)
    {
        printf("point R verify failed\n");
        return ret;
    }
    printf("point Rb verify sucessful\n");

    EC_POINT *Rtmp = EC_POINT_new(cs->ecGroup);
    EC_POINT_add(cs->ecGroup, Rtmp, cs->R, R_peer, cs->ctx);
    printf("点R (xa,ya):\n");
    dump_point(cs,Rtmp);
    BIGNUM *bntmp = BN_new();
    EC_POINT_get_affine_coordinates(cs->ecGroup, Rtmp, bntmp, NULL, cs->ctx);
    BIGNUM *e = BN_new();
    cs->r = BN_new();
    BN_bin2bn(E, 32, e);
    BN_mod_add_quick(cs->r, e, bntmp, cs->order);
    dump_bn("r:",cs->r);

    BN_mod_add_quick(bntmp, cs->k, cs->r, cs->order);

    BIGNUM *D = BN_new();
    BN_mod_inverse(D, cs->pri_comp, cs->order, cs->ctx);
    BN_mod_mul(st, bntmp, D, cs->order, cs->ctx);
    dump_bn("s`:",st);

    return 0;
}
int signPartnerAgreement(SM2CS *cs, BIGNUM *st,BIGNUM *t)
{

    BIGNUM *bntmp = BN_new();
    BN_mod_add_quick(bntmp, cs->k, st, cs->order);

    BIGNUM *D = BN_new();
    BN_mod_inverse(D, cs->pri_comp, cs->order, cs->ctx);
    BN_mod_mul(t, bntmp, D, cs->order, cs->ctx);
    dump_bn("t:",t);

    return 0;
}

int signInitiatorGetSign(SM2CS *cs, BIGNUM *t,BIGNUM *l_r, BIGNUM *s)
{

    dump_bn("r:",cs->r);
    BN_mod_sub(s, t, cs->r, cs->order, cs->ctx);
    BN_copy(l_r,cs->r);
    dump_bn("s:",s);

    return 0;
}
#endif
